<template>
  <div class="chunk-render-demo">
    <h1>分片渲染演示 (Vue 3 + TypeScript)</h1>

    <div class="controls">
      <div class="control-group">
        <label for="data-size">数据量 (条)</label>
        <input type="number" id="data-size" v-model.number="dataSize" min="10" max="100000" />
      </div>

      <div class="control-group">
        <label for="chunk-size">分片大小 (条)</label>
        <input type="number" id="chunk-size" v-model.number="chunkSize" min="1" max="1000" />
      </div>

      <div class="control-group">
        <label for="delay">模拟延迟 (ms)</label>
        <input type="number" id="delay" v-model.number="delay" min="0" max="1000" />
      </div>

      <button @click="startRendering" :disabled="isRendering">
        {{ isRendering ? '渲染中...' : '开始渲染' }}
      </button>
      <button @click="reset">重置</button>
    </div>

    <div class="status">
      <span>进度: {{ progress }}%</span>
      <span>用时: {{ Math.floor(elapsedTime) }}ms</span>
    </div>

    <div class="progress-container">
      <div class="progress-bar" :style="{ width: `${progress}%` }"></div>
    </div>

    <div class="item-list">
      <div
        v-for="(item, index) in visibleItems"
        :key="index"
        class="item"
        :class="{ odd: index % 2 === 0 }"
      >
        <span class="item-index">{{ index + 1 }}.</span> {{ item }}
      </div>
    </div>

    <div class="performance">
      <div class="metric">
        <h3>总渲染时间</h3>
        <p>{{ totalTime }}ms</p>
      </div>
      <div class="metric">
        <h3>分片数量</h3>
        <p>{{ chunkCount }}</p>
      </div>
      <div class="metric">
        <h3>每秒渲染元素</h3>
        <p>{{ elementsPerSecond }}/s</p>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed, reactive } from 'vue';
import { pageRender } from '@/utils';

interface RenderMetrics {
  totalTime: number;
  chunkCount: number;
  elementsPerSecond: number;
}

export default defineComponent({
  name: 'ChunkRenderDemo',
  setup() {
    // 响应式数据
    const dataSize = ref(1000);
    const chunkSize = ref(100);
    const delay = ref(0);
    const isRendering = ref(false);
    const progress = ref(0);
    const elapsedTime = ref(0);
    const visibleItems = ref<string[]>([]);

    const metrics = reactive<RenderMetrics>({
      // 总时间
      totalTime: 0,
      // 分片数量
      chunkCount: 0,
      // 每秒渲染元素数
      elementsPerSecond: 0
    });

    // 计算属性
    const totalTime = computed(() => metrics.totalTime.toFixed(2));
    const chunkCount = computed(() => metrics.chunkCount);
    const elementsPerSecond = computed(() => metrics.elementsPerSecond.toFixed(0));

    // 生成测试数据
    const generateTestData = (size: number): string[] => {
      return Array.from({ length: size }, (_, i) => `列表项 #${i + 1}`);
    };

    // 模拟延迟函数(非阻塞)
    const friendlyDelay = (delayMs: number): Promise<void> => {
      return new Promise((resolve) => {
        setTimeout(resolve, delayMs);
      });
    };

    // 分片渲染函数
    const renderChunk = async (
      data: string[],
      chunkSize: number,
      delayMs: number
    ): Promise<void> => {
      return new Promise((resolve) => {
        let index = 0;
        metrics.chunkCount = 0;
        const startTime = performance.now();

        const nextChunk = async () => {
          if (index >= data.length) {
            // 渲染完成
            const endTime = performance.now();
            metrics.totalTime = endTime - startTime;
            elapsedTime.value = metrics.totalTime;
            metrics.elementsPerSecond = data.length / (metrics.totalTime / 1000);
            isRendering.value = false;
            resolve();
            return;
          }

          // 渲染当前分片
          const chunk = data.slice(index, index + chunkSize);
          visibleItems.value.push(...chunk);

          // 模拟延迟
          await friendlyDelay(delayMs);

          index += chunkSize;
          metrics.chunkCount++;

          // 更新进度
          progress.value = Math.min(100, (index / data.length) * 100);

          // 使用 requestAnimationFrame 继续下一个分片
          requestAnimationFrame(nextChunk);
        };

        // 开始渲染
        nextChunk();
      });
    };

    // 开始渲染
    const startRendering = async (): Promise<void> => {
      if (isRendering.value) return;

      // 重置状态
      isRendering.value = true;
      progress.value = 0;
      elapsedTime.value = 0;
      visibleItems.value = [];
      metrics.totalTime = 0;
      metrics.chunkCount = 0;
      metrics.elementsPerSecond = 0;

      // 生成测试数据
      const testData = generateTestData(dataSize.value);

      // 开始分片渲染
      pageRender<string>(chunkSize.value, delay.value, testData, pushVisibleItems).then(
        (res: RenderMetrics) => {
          metrics.totalTime = res.totalTime;
          metrics.chunkCount = res.chunkCount;
          metrics.elementsPerSecond = res.elementsPerSecond;
          elapsedTime.value = res.totalTime;
          isRendering.value = false;
        }
      );
    };

    // 重置
    const reset = (): void => {
      isRendering.value = false;
      progress.value = 0;
      elapsedTime.value = 0;
      visibleItems.value = [];
      metrics.totalTime = 0;
      metrics.chunkCount = 0;
      metrics.elementsPerSecond = 0;
    };

    const pushVisibleItems = (items: string): void => {
      visibleItems.value = [...visibleItems.value, ...items];
      progress.value = Math.min(100, (visibleItems.value.length / 1000) * 100);
    };

    return {
      dataSize,
      chunkSize,
      delay,
      isRendering,
      progress,
      elapsedTime,
      visibleItems,
      totalTime,
      chunkCount,
      elementsPerSecond,
      startRendering,
      pushVisibleItems,
      reset
    };
  }
});
</script>

<style lang="scss" scoped>
// 定义颜色变量
$primary-color: #3498db;
$primary-hover: #2980b9;
$success-color: #2ecc71;
$text-color: #333;
$background-color: #f7f9fc;
$card-background: white;
$border-color: #ddd;
$odd-background: #f9f9f9;
$progress-background: #ecf0f1;
$metric-text: #7f8c8d;

.chunk-render-demo {
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  max-width: 1000px;
  margin: 0 auto;
  padding: 20px;
  background-color: $background-color;
  color: $text-color;

  h1 {
    text-align: center;
    color: #2c3e50;
    margin-bottom: 30px;
  }

  .controls {
    background: $card-background;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    align-items: center;

    .control-group {
      display: flex;
      flex-direction: column;
      gap: 5px;

      label {
        font-weight: 500;
        font-size: 14px;
      }

      input {
        padding: 10px 15px;
        border: 1px solid $border-color;
        border-radius: 4px;
        font-size: 16px;
      }
    }

    button {
      padding: 10px 15px;
      border: none;
      border-radius: 4px;
      font-size: 16px;
      background: $primary-color;
      color: white;
      cursor: pointer;
      transition: background 0.3s;
      font-weight: 500;

      &:hover {
        background: $primary-hover;
      }

      &:disabled {
        background: #95a5a6;
        cursor: not-allowed;
      }
    }
  }

  .status {
    display: flex;
    justify-content: space-between;
    margin-bottom: 15px;
    font-size: 14px;
  }

  .progress-container {
    width: 100%;
    background: $progress-background;
    border-radius: 4px;
    margin: 15px 0;
    overflow: hidden;
    height: 20px;

    .progress-bar {
      height: 100%;
      background: $success-color;
      transition: width 0.3s;
    }
  }

  .item-list {
    background: $card-background;
    border-radius: 8px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    padding: 20px;
    max-height: 400px;
    overflow-y: auto;

    .item {
      padding: 10px 15px;
      border-bottom: 1px solid #eee;
      display: flex;
      justify-content: flex-start;

      &.odd {
        background-color: $odd-background;
      }

      .item-index {
        color: $metric-text;
        font-weight: bold;
        margin-right: 10px;
      }
    }
  }

  .performance {
    margin-top: 20px;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 15px;

    .metric {
      background: $card-background;
      padding: 15px;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      text-align: center;

      h3 {
        margin: 0 0 10px 0;
        color: $metric-text;
        font-size: 14px;
      }

      p {
        margin: 0;
        font-size: 24px;
        font-weight: bold;
        color: #2c3e50;
      }
    }
  }
}
</style>
